本次周赛相对比较简单,前三题花的时间比较短,但无奈最后一题还是没思路。。。

将字符串拆分成若干长度为 k 的组

题目链接

将字符串拆分成若干长度为 k 的组

解题思路

遍历字符串 s 的每个字符并加入到一个临时字符串中,当此临时字符串长度为 k 时,加入到结果数组中并清空此字符串。若此时遍历到字符串的最后一个字符且此时临时字符串长度没有达到 k 时,则向其末尾填入字符 fill 直到临时字符串长度达到 k,再加入到结果数组中。

解题代码

class Solution {
public:
    vector<string> divideString(string s, int k, char fill) {
        vector<string> res;
        string newStr;
        for (int i = 0; i < s.size(); i++) {
            newStr += s[i];
            if (newStr.size() == k) {
                res.emplace_back(newStr);
                newStr.clear();
            }
            else if (i == s.size() - 1) {
                for (int j = newStr.size(); j < k; j++) {
                    newStr += fill;
                }
                res.emplace_back(newStr);
            }
        }
        return res;
    }
};

得到目标值的最少行动次数

题目链接

得到目标值的最少行动次数

解题思路

要想行动的次数最少,必然要在数尽可能大的情况下使用加倍,但这种思路不好正向模拟,因此考虑模拟该运算的逆过程。

从 target 值开始

  1. 若该数为奇数,则减 1 之后再除以 2,加倍次数 maxDoubles 减 1,行动次数加 2.
  2. 若该数为偶数,则除以 2,加倍次数 maxDoubles 减 1,行动次数加 1.

当加倍次数使用完毕,即 maxDoubles 值为 0 时,后面行动全为减 1,因此行动次数直接加上此时的 target - 1.

解题代码

class Solution {
public:
    int minMoves(int target, int maxDoubles) {
        int res = 0;
        while (target > 1) {
            if (maxDoubles > 0) {
                if (target % 2) {
                    target--;
                    target /= 2;
                    maxDoubles--;
                    res += 2;
                }
                else {
                    target /= 2;
                    maxDoubles--;
                    res++;
                }
            }
            else {
                res += target - 1;
                target = 1;
            }
        }
        return res;
    }
};

解决智力问题

题目链接

解决智力问题

解题思路

一个典型的动态规划问题,可将整个问题分解成一系列等价的子问题。首先确定最后一个问题单独出现时的最优解,然后逐个在该问题之前添加问题,该问题可以选择解或者不解:

  1. 若解,则此时结果为 questions[i][0] + dp[i + questions[i][1] + 1]dp[i] 为子问题 i ~ n 的解)。
  2. 若不解,则此时结果与不加入此问题时相同,为 dp[i + 1]

分别计算两种情况下的值,取更大的作为最优解。需要注意的是,dp[i + questions[i][1] + 1] 可能会导致数组越界,因此需要先进行判断。

按此方法依次迭代,最终得到原问题的最优解。

解题代码

class Solution {
public:
    long long mostPoints(vector<vector<int>>& questions) {
        vector<long long> dp(questions.size());
        dp.back() = questions.back().front();
        for (int i = questions.size() - 2; i >= 0; i--) {
            if (questions[i][1] + i + 1 < dp.size())
                dp[i] = max(questions[i][0] + dp[i + questions[i][1] + 1], dp[i + 1]);
            else
                dp[i] = max(static_cast<long long>(questions[i][0]), dp[i + 1]);
        }
        return dp[0];
    }
};

同时运行 N 台电脑的最长时间

题目链接

同时运行 N 台电脑的最长时间

解题思路

直接根据条件求解答案比较困难,因此考虑使用二分答案法,在所以可能的结果之间进行二分查找,每次查找完后验证是否符合条件。若符合,则 l = mid + 1;若不符合,r = mid. 最终找到第一个不符合条件的结果,它减一也就是符合条件的最大结果。

那么问题的关键在于如何确定某个数 t 是否符合条件。假设该数为 t,即所有电脑需要同时运行 t 分钟,因为一个电池只能同时给一台电脑供电,因此一个电池可供电的最长时间将会受到 t 的限制,因此一个电池的可供电的最长时间为 min(t, batteries[i])。根据这个条件,我们可以求出所有电池可供电的最长时间的总和 S,最后判断 S 是否大于等于 n * t 来得到该值是否符合条件。

为什么可以直接根据 S 和 n * t 的大小关系进行判断呢?我们可以将 N 台电脑的供电过程想象成一个串行的过程,先使用若干电池给第一台电脑供电,然后继续使用其它电池给第二台电脑供电,以此类推。事实上,各电脑的供电过程是并行的,而我们刚刚的电池分配方案间并不会冲突,因此是可行的。

解题代码

class Solution {
public:
    long long maxRunTime(int n, vector<int>& batteries) {
        auto canRun = [&](long long t) {
            long long sum = 0;
            for (const auto& battery : batteries) {
                sum += min(static_cast<long long>(battery), t);
            }
            return sum / t >= n;
        };

        long long sumBat = accumulate(batteries.begin(), batteries.end(), 0L);
        long long l = 1, r = sumBat / n + 1;
        while (l < r) {
            long long mid = (l + r) / 2;
            if (canRun(mid))
                l = mid + 1;
            else
                r = mid;
        }
        return l - 1;
    }
};